
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://openlayers.org/en/v5.3.0/css/ol.css" />
    <link rel="stylesheet" href="https://cdn.bootcss.com/openlayers/4.6.5/ol.css" type="text/css">
    <!-- <script type="text/javascript" src="https://openlayers.org/en/v5.3.0/build/ol.js"></script> -->
    <script src="https://cdn.bootcss.com/openlayers/4.6.5/ol.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <link href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js">
    </script>
    <title>地图</title>
</head>
<style>
    * {
        margin: 0;
        padding: 0
    }

    li {
        list-style: none
    }

    img {
        vertical-align: top;
        border: none
    }

    #map {
        position: relative;
        width: 100vw;
        height: 100vh;
    }

    .ul {
        display: flex;
        position: absolute;
        bottom: 30px;
        z-index: 999;
        background: white;
        padding: 5px;
        left: 30px;
        border: 1px solid blueviolet;
    }

    .ul li {
        margin: 0 10px;
        border: 1px solid;
        padding: 3px;
        cursor: pointer;
    }

    .ul .select {
        background: turquoise;
        color: white;
    }

    /**
    * 提示框的样式信息
    */
    .tooltip {
        position: relative;
        background: rgba(0, 0, 0, 0.5);
        border-radius: 4px;
        color: white;
        padding: 4px 8px;
        opacity: 0.7;
        white-space: nowrap;
    }

    .tooltip-measure {
        opacity: 1;
        font-weight: bold;
    }

    .tooltip-static {
        background-color: #ffffff;
        color: black;
        border: 1px solid white;
    }

    .tooltip-measure:before,
    .tooltip-static:before {
        border-top: 6px solid rgba(0, 0, 0, 0.5);
        border-right: 6px solid transparent;
        border-left: 6px solid transparent;
        content: "";
        position: absolute;
        bottom: -6px;
        margin-left: -7px;
        left: 50%;
    }

    .tooltip-static:before {
        border-top-color: #ffffff;
    }
</style>

<body>
    <div id='app'>
        <div id='map'>
            <ul class="ul">
                <li @click='ulevent(1)' :class="indexNum == 1 ? 'select' : ''">标记</li>
                <li @click='ulevent(2)' :class="indexNum == 2 ? 'select' : ''">圆形</li>
                <li @click='ulevent(3)' :class="indexNum == 3 ? 'select' : ''">多边形</li>
                <li @click='ulevent(4)' :class="indexNum == 4 ? 'select' : ''">删除</li>
                <li @click='ulevent(5)' :class="indexNum == 5 ? 'select' : ''">保存</li>
                <li @click='ulevent(6)' :class="indexNum == 6 ? 'select' : ''">测距</li>
                <li @click='ulevent(7)' :class="indexNum == 7 ? 'select' : ''">测面积</li>
                <li @click='ulevent(8)' :class="indexNum == 8 ? 'select' : ''">清除</li>
                <li @click='ulevent(9)' :class="indexNum == 9 ? 'select' : ''">全屏</li>
            </ul>
        </div>
        <div id="scalebar"></div>
    </div>
</body>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            indexNum: 0,
            ningbo: ol.proj.fromLonLat([121.5452456474304, 29.87544350296635]),
            map: null,
            fullScreenControl: null,
            //标记
            vectorSource: null,

            //画圆
            source: null,
            draw: null,
            state: false,

            // 测距 测面积
            //定义一个交互式绘图对象
            measuringdraw: null,
            measuringsource: null,
            measuringvector: null,
            //创建一个WGS84球体对象
            wgs84: null,
            //创建一个当前要绘制的对象
            sketch: null,
            //创建一个帮助提示框对象
            helpTooltipElement: null,
            //创建一个帮助提示信息对象
            helpTooltip: null,
            //创建一个测量提示框对象
            measureTooltipElement: null,
            //创建一个测量提示信息对象
            measureTooltip: null,
            //继续绘制多边形的提示信息
            continuePolygonMsg: 'Click to continue drawing the polygon',
            //继续绘制线段的提示信息
            continueLineMsg: 'Click to continue drawing the line'

        },
        methods: {
            ulevent(data) {
                this.indexNum = data
                this.map.removeInteraction(this.draw) //移除绘制图形控件
                this.map.removeInteraction(this.measuringdraw); // 移除测距
                switch (true) {
                    case data == 1: // 标记
                        // console.log(this.indexNum)
                        break;
                    case data == 2: // 圆形
                        this.state = true
                        this.addInteraction('Circle')
                        break;
                    case data == 3: // 多边形
                        this.state = true
                        this.addInteraction('Polygon')
                        break;
                    case data == 4: // 删除圆形
                        this.source.clear()
                        break;
                    case data == 5: // 保存截图
                        this.map.once('postcompose', (event) => {
                            var canvas = event.context.canvas
                            canvas.toBlob(function (blob) {
                                saveAs(blob, 'map2.png')
                            })
                        })
                        this.map.renderSync()
                        break;
                    case data == 6 || data == 7: // 测距侧面积
                        this.clearMeasure()
                        this.initData3()
                        //移除之前的绘制对象
                        this.map.removeInteraction(this.measuringdraw);
                        //重新进行绘制
                        this.measuringaddInteraction();
                        break;
                    case data == 8: // 清除
                        this.clearMeasure()
                        break;
                    case data == 9: // 全屏
                        this.indexNum = 0
                        let docElm = document.documentElement;
                        //W3C   
                        if (docElm.requestFullscreen) {
                            docElm.requestFullscreen();
                        }
                        //FireFox   
                        else if (docElm.mozRequestFullScreen) {
                            docElm.mozRequestFullScreen();
                        }
                        //Chrome等   
                        else if (docElm.webkitRequestFullScreen) {
                            docElm.webkitRequestFullScreen();
                        }
                        //IE11   
                        else if (elem.msRequestFullscreen) {
                            elem.msRequestFullscreen();
                        }

                        break;
                    default:
                        break
                }
            },
            // 添加设置标记样式
            createLabelStyle(feature) {
                return new ol.style.Style({
                    image: new ol.style.Icon({
                        anchor: [0.5, 60], //锚点
                        anchorOrigin: 'top-right', //锚点源
                        anchorXUnits: 'fraction', //锚点X值单位
                        anchorYUnits: 'pixels', //锚点Y值单位
                        offsetOrigin: 'top-right', //偏移原点
                        opacity: 0.75,
                        scale: 0.5,
                        src: './daiban.png' //图标的URL
                    }),
                    //   text: new ol.style.Text({
                    //       textAlign: 'center',            //位置
                    //       textBaseline: 'middle',         //基准线
                    //       font: 'normal 14px 微软雅黑',    //文字样式
                    //       text: feature.get('name'),      //文本内容
                    //       fill: new ol.style.Fill({       //文本填充样式（即文字颜色)
                    //           color: '#000'
                    //       }),
                    //       stroke: new ol.style.Stroke({
                    //           color: '#F00', 
                    //           width: 2
                    //       })
                    //   })
                });
            },
            // 添加圆形绘图
            addInteraction(data) {
                if (this.state) {
                    // this.source.clear();
                    // let geometryFunction, maxPoints;
                    // if (typeValue === 'Square') { //正方形
                    //     typeValue = 'Circle'; //设置绘制类型为Circle
                    //     //设置几何信息变更函数，即创建正方形
                    //     geometryFunction = ol.interaction.Draw.createRegularPolygon(4);
                    // }
                    //实例化图形绘制控件对象并添加到地图容器中
                    this.draw = new ol.interaction.Draw({
                        source: this.source,
                        type: data, //几何图形类型
                        // geometryFunction: this.geometryFunction, //几何信息变更时的回调函数
                        maxPoints: null //最大点数
                    });
                    this.map.addInteraction(this.draw);
                    this.state = false
                }
            },
            // =========================================== 初始化标记 ========================================
            initData() {
                //矢量标注的数据源
                this.vectorSource = new ol.source.Vector({
                    features: []
                });
                //矢量标注图层
                let vectorLayer = new ol.layer.Vector({
                    source: this.vectorSource
                });

                this.map.addLayer(vectorLayer);
            },
            // =========================================== 初始化圆形 ========================================
            initData2() {
                //实例化一个矢量图层Vector作为绘制层
                this.source = new ol.source.Vector();
                let vectorLayer = new ol.layer.Vector({
                    source: this.source,
                    style: new ol.style.Style({
                        fill: new ol.style.Fill({ //填充样式
                            color: 'rgba(255, 255, 255, 0.2'
                        }),
                        stroke: new ol.style.Stroke({ //线样式
                            color: '#00c033',
                            width: 2
                        }),
                        image: new ol.style.Circle({ //点样式
                            radius: 70,
                            fill: new ol.style.Fill({
                                color: '#00c033'
                            })
                        })
                    })
                });
                //将绘制层添加到地图容器中
                this.map.addLayer(vectorLayer);
            },
            // =========================================== 初始化测距面积 ========================================
            // 测距测面积
            initData3() {
                this.wgs84 = new ol.Sphere(6378137)
                this.sketch = new ol.Feature()
                this.measuringsource = new ol.source.Vector()
                this.measuringvector = new ol.layer.Vector({
                    source: this.measuringsource,
                    style: new ol.style.Style({
                        fill: new ol.style.Fill({
                            color: 'rgba(255,255,255,0.2)'
                        }),
                        stroke: new ol.style.Stroke({
                            color: '#e21e0a',
                            width: 2
                        }),
                        image: new ol.style.Circle({
                            radius: 5,
                            fill: new ol.style.Fill({
                                color: '#ffcc33'
                            })
                        })
                    })
                });
                this.map.addLayer(this.measuringvector)
                let scaleLineControl = new ol.control.ScaleLine({
                    units: 'metric',
                    target: 'scalebar',
                    className: 'ol-scale-line'
                });
                this.map.addControl(scaleLineControl);

            },
            //格式化测量长度
            formatLength(line) {
                //定义长度变量
                let length;

                //计算平面距离
                length = Math.round(line.getLength() * 100) / 100;
                //定义输出变量
                let output;
                //如果长度大于1000，则使用km单位，否则使用m单位
                if (length > 1000) {
                    output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km'; //换算成KM单位
                } else {
                    output = (Math.round(length * 100) / 100) + ' ' + 'm'; //m为单位
                }
                return output;
            },
            //格式化测量面积
            formatArea(polygon) {
                //定义面积变量
                let area;

                area = polygon.getArea();
                //定义输出变量
                var output;
                //当面积大于10000时，转换为平方千米，否则为平方米
                if (area > 10000) {
                    output = (Math.round(area / 1000000 * 100) / 100) + ' ' + 'km<sup>2</sup>';
                } else {
                    output = (Math.round(area * 100) / 100) + ' ' + 'm<sup>2</sup>';
                }
                return output;
            },
            //添加测量面积距离
            measuringaddInteraction() {
                this.map.on('pointermove', (evt) => {
                    //如果是平移地图则直接结束
                    if (evt.dragging) {
                        return;
                    }
                    //帮助提示信息
                    // let helpMsg = '绘图开始提示信息';

                    if (this.sketch) {
                        //获取绘图对象的几何要素
                        var geom = this.sketch.getGeometry();
                        // //如果当前绘制的几何要素是多边形，则将绘制提示信息设置为多边形绘制提示信息
                        // //如果当前绘制的几何要素是多线段，则将绘制提示信息设置为多线段绘制提示信息
                        // if (geom instanceof ol.geom.Polygon) {
                        //     helpMsg = this.continuePolygonMsg;
                        // } else if (geom instanceof ol.geom.LineString) {
                        //     helpMsg = this.continueLineMsg;
                        // }
                    }

                    // //设置帮助提示要素的内标签为帮助提示信息
                    // this.helpTooltipElement.innerHTML = helpMsg;
                    // //设置帮助提示信息的位置
                    // this.helpTooltip.setPosition(evt.coordinate);
                    //移除帮助提示要素的隐藏样式
                    $(this.helpTooltipElement).removeClass('hidden');
                });
                $(this.map.getViewport()).on('mouseout', () => {
                    $(this.helpTooltipElement).addClass('hidden');
                });
                let that = this
                // 获取当前选择的绘制类型
                if (this.indexNum == 6) {
                    type = 'LineString'
                }
                if (this.indexNum == 7) {
                    type = 'Polygon'
                }
                //创建一个交互式绘图对象
                this.measuringdraw = new ol.interaction.Draw({
                    //绘制的数据源
                    source: this.measuringsource,
                    //绘制类型
                    type: type,
                    //样式
                    style: new ol.style.Style({
                        fill: new ol.style.Fill({
                            color: 'rgba(255,255,255,0.2)'
                        }),
                        stroke: new ol.style.Stroke({
                            color: 'rgba(0,0,0,0.5)',
                            lineDash: [10, 10],
                            width: 2
                        }),
                        image: new ol.style.Circle({
                            radius: 5,
                            stroke: new ol.style.Stroke({
                                color: 'rgba(0,0,0,0.7)'
                            }),
                            fill: new ol.style.Fill({
                                color: 'rgba(255,255,255,0.2)'
                            })
                        })
                    })
                });
                //将交互绘图对象添加到地图中
                this.map.addInteraction(this.measuringdraw);

                //创建测量提示框
                this.createMeasureTooltip();
                //创建帮助提示框
                this.createHelpTooltip();

                //定义一个事件监听
                let listener;
                //定义一个控制鼠标点击次数的变量
                let count = 0;
                //绘制开始事件
                this.measuringdraw.on('drawstart', (evt) => {
                    //The feature being drawn.
                    this.sketch = evt.feature;
                    //提示框的坐标
                    let tooltipCoord = evt.coordinate;
                    //监听几何要素的change事件
                    //Increases the revision counter and dispatches a 'change' event.

                    listener = this.sketch.getGeometry().on('change', function (evt) {
                        //The event target.
                        //获取绘制的几何对象
                        let geom = evt.target;
                        //定义一个输出对象，用于记录面积和长度
                        let output;
                        if (geom instanceof ol.geom.Polygon) {
                            that.map.removeEventListener('singleclick');
                            that.map.removeEventListener('dblclick');
                            //输出多边形的面积
                            output = that.formatArea(geom);
                            //获取多变形内部点的坐标
                            tooltipCoord = geom.getInteriorPoint().getCoordinates();
                        } else if (geom instanceof ol.geom.LineString) {
                            //输出多线段的长度
                            output = that.formatLength(geom);
                            //获取多线段的最后一个点的坐标
                            tooltipCoord = geom.getLastCoordinate();
                        }

                        //设置测量提示框的内标签为最终输出结果
                        that.measureTooltipElement.innerHTML = output;
                        //设置测量提示信息的位置坐标
                        that.measureTooltip.setPosition(tooltipCoord);
                    });

                    //地图单击事件
                    this.map.on('singleclick', (evt) => {
                        //设置测量提示信息的位置坐标，用来确定鼠标点击后测量提示框的位置
                        this.measureTooltip.setPosition(evt.coordinate);
                        //如果是第一次点击，则设置测量提示框的文本内容为起点
                        if (count == 0) {
                            this.measureTooltipElement.innerHTML = "起点";
                        }
                        //根据鼠标点击位置生成一个点
                        let point = new ol.geom.Point(evt.coordinate);
                        //将该点要素添加到矢量数据源中
                        this.source.addFeature(new ol.Feature(point));
                        //更改测量提示框的样式，使测量提示框可见
                        this.measureTooltipElement.className = 'tooltip tooltip-static';
                        //创建测量提示框
                        this.createMeasureTooltip();
                        //点击次数增加
                        count++;
                    });

                    //地图双击事件
                    this.map.on('dblclick', (evt) => {
                        //根据
                        let point = new ol.geom.Point(evt.coordinate);
                        this.source.addFeature(new ol.Feature(point));
                    });
                });
                //绘制结束事件
                this.measuringdraw.on('drawend', (evt) => {
                    count = 0;
                    //设置测量提示框的样式
                    this.measureTooltipElement.className = 'tooltip tooltip-static';
                    //设置偏移量
                    this.measureTooltip.setOffset([0, -7]);
                    //清空绘制要素
                    this.sketch = null;
                    //清空测量提示要素
                    this.measureTooltipElement = null;
                    //创建测量提示框
                    this.createMeasureTooltip();
                    //Removes an event listener using the key returned by on() or once().
                    //移除事件监听
                    ol.Observable.unByKey(listener);
                    //移除地图单击事件
                    this.map.removeEventListener('singleclick');
                });
            },
            // 创建提示框
            createHelpTooltip() {
                //如果已经存在帮助提示框则移除
                if (this.helpTooltipElement) {
                    this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
                }
                //创建帮助提示要素的div
                this.helpTooltipElement = document.createElement('div');
                //设置帮助提示要素的样式
                this.helpTooltipElement.className = 'tooltip hidden';
                //创建一个帮助提示的覆盖标注
                this.helpTooltip = new ol.Overlay({
                    element: this.helpTooltipElement,
                    offset: [15, 0],
                    positioning: 'center-left'
                });
                //将帮助提示的覆盖标注添加到地图中
                this.map.addOverlay(this.helpTooltip);
            },
            // 创建内容框
            createMeasureTooltip() {
                //创建测量提示框的div
                this.measureTooltipElement = document.createElement('div');
                this.measureTooltipElement.setAttribute('id', 'lengthLabel');
                //设置测量提示要素的样式
                this.measureTooltipElement.className = 'tooltip tooltip-measure';
                //创建一个测量提示的覆盖标注
                this.measureTooltip = new ol.Overlay({
                    element: this.measureTooltipElement,
                    offset: [0, -15],
                    positioning: 'bottom-center'
                });
                //将测量提示的覆盖标注添加到地图中
                this.map.addOverlay(this.measureTooltip);
            },
            // 清空测距
            clearMeasure() {
                this.map.removeInteraction(this.measuringdraw);
                this.source.clear()
                this.map.removeLayer(this.measuringvector)

                let nodelist = document.getElementsByClassName('ol-overlay-container')
                for (let i = 0; i < nodelist.length; i++) {
                    let element = document.getElementById("lengthLabel");
                    if (element) {
                        element.parentNode.removeChild(element);
                    }
                }
            }
        },
        created() {

        },
        mounted() {
            this.map = new ol.Map({
                target: 'map',
                // layers: [
                //     new ol.layer.Tile({
                //         source: new ol.source.OSM()
                //     })
                // ],
                view: new ol.View({
                    center: this.ningbo,
                    zoom: 15
                })
            });
            let offlineMapLayer = new ol.layer.Tile({
                source: new ol.source.XYZ({
                    url: 'http://192.168.0.4:72/map/mapabc/roadmap/{z}/{x}/{y}.png' //maptile为下载的瓦片地图文件夹,它会自动识别
                })
            });
            this.map.addLayer(offlineMapLayer)
            this.initData()
            this.initData2()
            this.initData3()
            // this.fullScreenControl = new ol.control.FullScreen()
            // this.map.addControl(this.fullScreenControl)
            this.map.on('click', (evt) => {
                let feature = this.map.forEachFeatureAtPixel(evt.pixel, function (feature, layerVetor) {
                    return feature;
                });
                // 点击时是否是有标记
                if (feature) {
                    // let popup = new ol.Overlay({
                    //     element: container,
                    //     autoPan: true,
                    //     positioning: 'bottom-center',
                    //     stopEvent: false,
                    //     autoPanAnimation: {
                    //         duration: 250
                    //     }
                    // })
                    // if (popup.getPosition() == undefined) {
                    //     popup.setPosition(coordinate); //设置popup的位置
                    // }

                    console.log(feature.getProperties(), 111111)
                } else {
                    console.log(222)
                }
                switch (this.indexNum) {
                    // 标记
                    case 1:
                        // console.log(ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326'));
                        var coordinate = evt.coordinate; //鼠标单击点的坐标
                        //新建一个要素ol.Feature
                        var newFeature = new ol.Feature({
                            geometry: new ol.geom.Point(coordinate), //几何信息
                            name: '标注点1111111',
                            lat: '1233',
                            lon: '14566'
                        });
                        newFeature.setStyle(this.createLabelStyle(newFeature)); //设置要素样式

                        // console.log(vectorSource, 1)
                        this.vectorSource.clear() // 清除    
                        this.vectorSource.addFeature(newFeature);
                        break;
                    default:
                        break
                }

            });
        }
    })

    // ===========================================标记========================================
    // //这样就实现了预先加载图文标注
    // var iconFeature = new ol.Feature({
    //     geometry: new ol.geom.Point(ningbo),
    //     name: '宁波', //名称属性
    //     population: 2115 //人口数（万）
    // });
    // //设置点要素样式
    // iconFeature.setStyle(createLabelStyle(iconFeature));
</script>

</html>